Содержание

Отказ от ответственности

Настоящий отказ от ответственности регулирует использование этого отчета. Ваше чтение остальной части отчета, в дополнение к данному отказу от ответственности, означает ваше согласие с его условиями в полном объеме.

Хотя автор отчета (далее - эксперт) сделал все возможное для обеспечения точности и достоверности информации, представленной в этом отчете, вся информация предоставляется «как есть».

Эксперт не дает никаких гарантий, обещаний и/или заявлений любого рода, выраженных или подразумеваемых, в отношении точности, содержания, полноты, обоснованности, своевременности, достоверности (или обратного), а также пригодности (или непригодности) для ваших конкретных обстоятельств информации, предоставленной в данном отчете, любых ошибок или упущений, потерь или ущерба любой природы (прямого, косвенного, вытекающего или иного) независимо от того, возникают ли они в договоре, деликте или иным образом, или которые могут возникнуть в результате использования (или не использования) этой информации, даже если эксперт был проинформирован о возможности такого ущерба.

В отчете содержатся ссылки на другие сайты, принадлежащие третьим сторонам. Содержание таких сторонних сайтов не находится под контролем эксперта, и эксперт не несет ответственности за информацию или содержание этих сайтов. Ссылки на такие сторонние сайты не должны восприниматься в качестве рекомендации экспертом стороннего сайта или любых продуктов, рекламируемых, предлагаемых или продаваемых на сторонних сайтах, а также в качестве гарантии того, что на таких сайтах нет компьютерных вирусов или любых других элементов деструктивного свойства.

Введение

22 апреля 2016 года один из заметных (37,305 последователей на июль 2017 года) казахстанских блоггеров - Асель Баяндарова опубликовала пост в Facebook под названием «Что можно делать казашкам». Пост сопровождался фотографией Асель только в зеленых плавках (почти) без демонстрации интимных частей тела.

Этот пост вызвал ожесточенную дискуссию между пользователями Facebook из Казахстана (и за рубежом), которые разделились на две группы, одна из которых поддерживала автора поста, а другая обвиняла ее вплоть до использования обсценных (матерных) слов.

В целом этот пост получил более 20 000 лайков, 990 перепостов и примерно 11 000 комментариев + ответов.

Цель настоящего проекта - сделать исследовательский и анализ тональности текста комментариев к этому посту.

Проект выполнен исключительно в образовательных целях, не финансировался и не преследует никаких иных целей, кроме получения дополнительной экспертизы в аналитической обработке публично доступной информации.

Получение данных

Весь процесс получения и очистки данных не является ни оптимальным (даже близко), ни легко воспроизводимым в его текущем состоянии. Разработка метода получения / очистки таких данных оптимальным образом стала отдельным проектом. Если вас интересует эта тема, пожалуйста, свяжитесь со мной, используя любые социальные контакты, предоставленные в конце отчета.

Все комментарии и ответы под исследуемым постом были открыты вручную, скопированы и вставлены в Sublime Text 3 для последующей обработки.

Существует специальный букмарклет, который позволяет автоматически открывать все комментарии, скрытые за дополнительными ссылками, но вы должны знать, что на определенном моменте Facebook перестает выдавать комментарии, скрытые за ссылкой Открыть предыдущие комментарии. Вы нажимаете на нее и ничего не происходит. Этот букмарклет сталкивается с таким же ограничением.

Год спустя, в 2017 году, я добавил несколько свежих комментариев, которые люди все еще делали под этим постом. Файл с исходным текстом содержит 34890 строк и занимает 2,3 МБ дискового пространства.

Ниже приведен образец необработанного текстового файла, содержащего всю информацию.

Erzhan Malkovich
Erzhan Malkovich мя саган...
· 5 · April 22 at 1:32am
Alexander Gutin
Alexander Gutin Вы, дураки, гордиться должны, что казашки красавицы, а не глаза выпучивать и ножками топать.
· 58 · April 22 at 1:34am
Ayan Kaliahmet
Ayan Kaliahmet мы и гордимся красавицами но не шлюхами...
· 1 · April 22 at 5:21am

...

Константин Шакиров
Константин Шакиров Смысл обсуждать то,о чём уже отшумели летние дожди??)))))
Like · Reply · April 23 at 09:00am
Berlin Irisheff
Berlin Irisheff С такой чудесной фигурой позволительно рассуждать о чем угодно.
Like · Reply · 1 · April 23 at 12:00am
Omir Shynybekuly
Omir Shynybekuly 20 148 лайков...
Наверное это рекорд для казахского сегмента Фб?
Like · Reply · April 23 at 12:00am

Очистка данных

Как видно из приведенного выше примера, общая структура текста выглядит следующим образом:

  1. FirstName LastName + ‘\ n’
  2. Тело комментария, всегда начинается с FirstName LastName + text + ‘\ n’
    • может быть пустым или содержать слово «фото», если в качестве комментария использовалось только изображение
  3. Специальный символ (точка, Unicode ‘ U00B7’), за которым следует одно из двух:
    • Количество понравившихся + точка + Месяц Дата с одной или двумя цифрами + ‘at’ + время + ‘\ n’
    • Месяц Дата с одной или двумя цифрами + ‘at’ + время + ‘\ n’

Sublime Text 3 имеет очень удобный инструмент поиска / замены, который поддерживает регулярные выражения:

Ниже вы найдете все команды и строки поиска регулярных выражений, которые были использованы шаг за шагом, чтобы найти и заменить / удалить определенные текстовые конструкции.

16 lines with unopened reply comment
^\d reply
delete them

dates without year, these are only present in fresh comments made in 2017
^(·\s\w+\s\d{1,2}) at
replace with (adds year)
\1, 2017 at

lines without any likes, they start with bod + space + capital letter (month name)
^·\s([A-Z]{1}) 
replace with
· 0 · \1
so that all similar constructions now look alike:
· 5 · April 22 at 5:24am

simplify previous string to ease further processing:
search for
^·\s(\d+)\s·\s(\w+)
replace with
· \1\n· \2
to split it into two strings and get
· 5
· April 22 at 5:24am

empty lines between first and last names
^(\w+)\s(\w+)\n\s
\1\s\2\n
delete them

stuck lines, about 150 entries, need to delete manually :)
[ap]m\n\w+\s\w+

smiles and other emoticons excluding letters, numbers, punctuation etc
[^a-zA-Z0-9а-яА-Яё:.,\-='"()·!?Ұүқôәіңғ\s]
copy them all and save to a different file for further counting / processing

Дополнительные регулярные выражения, которые использовались и могут оказаться полезными при обработке этого файла:

lines containing "· 2 · April 22"
^(\s*.\s*[a-zA-Z0-9]*\s.*\s[a-zA-Z0-9]*\s*\d*\sat\s\d*:\d*(am|pm))

lines containing "· 2 ·"
^(\·\s*\d*\s.*)

lines containing "· Edited"
^(\s*\·\s*Edited)

lines with first last names only
^\w+\s+\w+\n

first and double last name, which is connected by "-"
^\w+\s+\w+-\w+\n

double, triple etc newlines
^\n{2,}

somebody's photo.
^[A-ZА-Я]\w{2,}\s*[A-ZА-Я]\w*'s photo.

first and last names in the beginning, including russian and kazakh (diacritic) letters
^[A-ZА-ЯҚƏ]\w{2,}(\s+|\.?)[A-ZА-ЯҚƏ]\w*\s+

В целом эта часть занимала большую часть времени и включала в себя много ручной работы. Файл с обработанным текстом содержит 55145 строк и занимает 2,3 МБ дискового пространства. Вот образец файла после завершения очистки:

Erzhan Malkovich
Erzhan Malkovich мя саган...
· 5
· April 22, 2016 at 1:32am

Alexander Gutin
Alexander Gutin Вы, дураки, гордиться должны, что казашки красавицы, а не глаза выпучивать и ножками топать.
· 58
· April 22, 2016 at 1:34am

Ayan Kaliahmet
Ayan Kaliahmet мы и гордимся красавицами но не шлюхами...
· 1
· April 22, 2016 at 5:21am

...

Константин Шакиров
Константин Шакиров Смысл обсуждать то,о чём уже отшумели летние дожди??)))))
· 0
· April 23, 2017 at 09:00am

Berlin Irisheff
Berlin Irisheff С такой чудесной фигурой позволительно рассуждать о чем угодно.
· 1
· April 23, 2017 at 12:00am

Omir Shynybekuly
Omir Shynybekuly 20 148 лайков...
Наверное это рекорд для казахского сегмента Фб?
· 0
· April 23, 2017 at 12:00am

Пред-обработка

Для предварительной обработки подготовленного файла был написан специальный парсер. Зная структуру подготовленного файла, мы можем пройти через него построчно, сохраняя необходимые фрагменты информации в таблицу.

# Information parser for pre-processed file
# (c) 2017 Denis Rasulev
# All Rights Reserved

parse_comments <- function(comments) {
     # this function goes through pre-processed comments file row by row,
     # finds information by certain markers and saves it to data frame as
     # name, text of comment, number of likes and date posted
     # returns data.frame['name','cmnt','like','year',''month','day','hour']

     # load required libraries
     library(lubridate)  # Make Dealing with Dates a Little Easier

     # save length of file with comments
     number_of_rows <- length(comments)

     # prepare empty data frame to store name, comment, likes and dates
     df <- data.frame(matrix(ncol = 4, nrow = number_of_rows))
     colnames(df) <- c('name','cmnt','like','date')

     for (i in 1:number_of_rows ) {

          # if row is empty...
          if ( comments[i] == "" ) {

               # then next row contains commenter's name
               df[i, 'name'] <- comments[i + 1]

               # third row after empty one contains text of a comment and
               # it always starts with the name of a commenter so we remove it
               comment_text <- sub(paste0(comments[i + 1],' '), '', comments[i + 2])

               # text of a comment may be on several lines so we need index
               # to read them all
               j <- 3

               # while next line doesn't start with middle dot '·' (unicode 00B7)
               while (substring(comments[i + j], 1, 1) != '\U00B7') {

                    # check if we have reached end of the file where we need to
                    # break the loop
                    if ( i + j > number_of_rows ) {
                         break
                    }

                    # if not end then add every line to comment
                    comment_text <- paste(comment_text, comments[i + j])
                    j <- j + 1
               }

               # save complete text of a comment
               df[i, 'cmnt'] <- comment_text

               # save number of likes for a comment, removing midle dot
               df[i, 'like'] <- sub('\U00B7 ', '', comments[i + j])

               # save date when a comment was posted, removing midle dot
               df[i, 'date'] <- sub('\U00B7 ', '', comments[i + j + 1])
          }
     }

     # remove empty rows, consisting only of NAs
     df <- na.omit(df)

     # convert number of likes from character to number
     df[,'like'] <- as.numeric(df[,'like'])

     # split date column for convenience of further analysis
     df[,'dt']    <- parse_date_time(df[,'date'], orders = "mdy IMp")
     df[,'year']  <- year(df[,'dt'])
     df[,'month'] <- month(df[,'dt'])
     df[,'day']   <- day(df[,'dt'])
     df[,'hour']  <- hour(df[,'dt'])

     # remove unused columns
     df[,c('date','dt')] <- NULL

     # return clean data frame
     return(df)
}

Анализ занимает некоторое время и возвращает чистые и аккуратные данные в формате таблицы (data frame).

C этой точки анализ данных становится полностью воспроизводимым. В директории data репозитория на GitHub можно найти предобработанные файлы с комментариями в форрматах .csv и .rds. Файл .csv имеет следующую структуру:

Настройка и загрузка

Для последующего анализа нам необходимо подготовить рабочее пространство, загрузить необходимые библиотеки и данные.

# Facebook Comments Exploration and Analysis
# (c) 2017 Denis Rasulev
# All Rights Reserved

# load required libraries and functions
library(tm)         # Framework for text mining applications within R
library(NLP)        # Basic classes and methods for Natural Language Processing
library(ggplot2)    # Implementation of the grammar of graphics in R
library(wordcloud2) # Fast visualization tool for creating wordcloud
source("/Volumes/data/projects/fb_sentiment/code/parser.r")
source("/Volumes/data/projects/fb_sentiment/code/helper.r")

# if parsed file does not exist
if (!file.exists("/Volumes/data/projects/fb_sentiment/data/comments.rds")) {

     # then load pre-processed comments file
     comments_file  <- readLines("/Volumes/data/projects/fb_sentiment/data/comments_processed.txt",
                                 encoding = "UTF-8", skipNul = FALSE, warn = FALSE)

     # parse everything from it
     parsed <- parse_comments(comments_file)

     # and save it to disk
     saveRDS(parsed, file = "/Volumes/data/projects/fb_sentiment/data/comments.rds")

     # clear memory
     rm(comments_file, parse_comments, parsed)
}

# if parsed file already exists, read it in
df_comments <- readRDS("/Volumes/data/projects/fb_sentiment/data/comments.rds")

Исследование 1 - Временной анализ

Было интересно узнать, насколько долго “живет” пост, вызвавший такое оживленное обсуждение. Другими словами, как долго пользователи продолжают оставлять комментарии под этим постом. Для начала посмотрим на данные в масштабе годов.

# aggregate data by time frame
t1 <- table(df_comments$year)   # year
t2 <- table(df_comments$month)  # month
t3 <- table(df_comments$day)    # day
t4 <- table(df_comments$hour)   # hour

# distribution of comments by year
par(mar = c(2,4,4,1) + 0.1)
barplot(t1,
        col = "lightgreen",
        ylim = c(0,12000),
        las = 1)
title("Количество комментариев в год", adj = 0.5, line = 2)

Как мы видим, основное количество комментариев было сделано в 2016 году, но некоторый незначительный интерес по-прежнему сохранялся и в 2017 году. Тогда посмотрим, как количество комментариев распределено по месяцам.

# distribution of comments by month
par(mar = c(2,4,4,1) + 0.1)
barplot(t2,
        col = "lightgreen",
        ylim = c(0,12000),
        las = 1)
title("Количество комментариев в месяц", adj = 0.5, line = 2)

Видим, что подавляющее число комментариев сделано в апреле. После этого число комментариев резко падает. Таким образом, можно сделать вывод, что популярность поста не превышала один месяц. Ок, теперь надо посмотреть в разрезе дней.

# distribution of comments by day
par(mar = c(2,4,4,1) + 0.1)
barplot(t3,
        col = "lightgreen",
        ylim = c(0,5000),
        las = 1)
title("Количество комментариев в день", adj = 0.5, line = 2)

Тут мы можем четко видеть, что пик комментариев пришелся на первые несколько дней, в основном с 22 (максимум) до 26 апреля, т.е. пользователи Facebook активно выражали свои идеи и эмоции, связанные с этим постом, в течении 4 дней. И хотя это довольная значительная активность, мы можем сделать вывод, что даже самые эмоциональные посты не “живут” дольше нескольких дней.

Если вы работаете с социальными сетями, то вам необходимо принять данный факт во внимание и, в случае необходимости, поддерживать “жизнь” вашего поста дополнительными средствами/активностью.

Далее было интересно посмотреть на активность пользователей в течении дня. В какое время происходило наиболее активное комментирование?

# distribution of comments by hour
par(mar = c(2,4,4,1) + 0.1)
barplot(t4,
        col = "lightgreen",
        ylim = c(0,800),
        las = 1)
title("Количество комментариев в час", adj = 0.5, line = 2)

Активность по часам показывает несколько интересных результатов. Наиболее активно пользователи комментировали в первой половине дня, главным образом с 8 утра до 12 часов дня, после чего активность резко снижается (время обеда?). После этого мы можем видеть второй, более низкий пик активности, который приходится на период с 7 вечера и до 12 ночи.

Большое количество комментариев, сделанных с полуночи до 8 утра (левая часть графика) связано с тем, что большинство из них поступало почти непрерывно в первые 48 часов после размещения поста.

Исследование 2 - Смайлики и слова

Одно из целей было проанализировать основные слова и эмотиконы (значки, выражающие эмоции), использованные в комментариях.

Люди использовали эмотиконы в своих комментариях довольно активно. Для анализа они были условно разделены на “позитивные” и “негативные”. Для полноты картины необходимо понимать, что часть смайликов использовались с иронией, т.е. имели прямо противоположный смысл. Однако процент таких случаев был настолько незначительным, что ими можно пренебречь. Вот статистическое распределение смайликов по группам.

Смайлики

Позитивные Частота Негативные Частота
:) 914 👎 133
👍 621 :( 91
😂 578 😡 63
👏 189 🙈 52
😍 54 😱 51
😊 44 😈 16
👌 42 👊 15
😁 40 😠 14
😀 35 😕 13
😘 18
2535 448

Можно видеть, что число позитивных смайликов значительно больше негативных. Можем сделать вывод, что большинство пользователей выражали свою поддержку автору поста, а также другим комментаторам.

Теперь давайте посмотрим статистические данные о словах, использованных для выражения своего отношения к данному посту. Слова искались вручную, посредством регулярных выражений, так как не существует (известных автору) словарей с позитивными / негативными словами для русского и казахского языков.

Через косую черту перечислены однокоренные слова и/или разные написания одних и тех же слов. Например, “смело/ая” означает, что в одну категорию считались слова “смело” и “смелая”.

Слова

Позитивные Частота Негативные Частота
красавица/отка/ивая/ота/ивое 454 Ұят/уят/сыз 415
молодец/чина/чинка 357 намыс/сыз 171
смело/ая 272 позор 143
супер 114 стыд/но 141
браво 84 дура 140
поддерживаю 69 ужас/но 71
умная 64 шлюха (+варианты) 48
респект 43 фу 42
круто/ая 35 проститутка 23
секси 32 глупая 15
отличная 24 тупая 15
шикарная 22 сука 15
симпатичная 14 блядь (+варианты) 11
прелесть 10 дешевка/ая 10
богиня 5 тьфу 10
курица 8
девка 6
уродка 2
1599 1286

Как видно из таблицы выше, между позитивными и негативными словами наблюдается практически паритет с небольшим преимуществом (55% против 45%) в общем количестве позитивных слов. Негативные слова отличались чуть большим разнообразием.

Из-за характера фотографии было подозрение об активном использовании специальных слов, описывающих интимные части тела. Однако, как показывают данные, в комментариях они использовались относительно редко.

Специальные слова

Слова Частота
попа/жопа 47
сиськи 31
тема сисек 25

Таким образом, можем сделать вывод, что комментарии разделились на позитивные и негативные приблизительно пополам, с небольшим преимуществом в сторону позитивных.

Исследование 3 - Топ 30 объектов

В этой части мы проведем количественный анализ текста. В первую очередь интересно было посмотреть, кто был самым активном участником дискусии.

# top commenters by number of comments
t <- as.data.frame(table(df_comments$name))
t <- t[order(t$Freq, decreasing = TRUE),]
names(t)[1] = 'name'
names(t)[2] = 'comments'

# show top commenters as bar plot
par(mar = c(2,12,3,1) + 0.1)
barplot(t$comments[1:30],
        names.arg = t$name[1:30],
        col = rainbow(45),
        xlim = c(0,300),
        ylim = c(35,0),
        horiz = TRUE,
        las = 1)
grid(NULL, NA, lwd = 1, col = "lightgray", lty = "dotted")
title("Топ 30 по числу комментариев", adj = 0, line = 0.5)

Лидером по количеству сделанных комментариев является Yermek Smailov с 277 комментариями. Второе место принадлежит Андрей Съедин с 87 комментариями и на третьем месте Venera Tumenova с 71 комментариями.

Теперь давайте посмотрим, чей комментарий получил наибольшее количество лайков.

# most liked commenters
v <- df_comments[order(df_comments$like, decreasing = TRUE),]

# show top most liked as bar plot
par(mar = c(3,12,3,1) + 0.1)
barplot(v$like[1:30],
        names.arg = v$name[1:30],
        col = "lightgreen",
        xlim = c(0,100),
        ylim = c(35,0),
        horiz = TRUE,
        las = 1)
grid(NULL, NA, lwd = 1, col = "lightgray", lty = "dotted")
title("Топ 30 по полученным лайкам", adj = 0, line = 0.5)

Абсолютным лидером является Секе Қозған, чей комментарий получил 88 лайков. На втором месте находится комментарий Sergey Bolbat с 68 лайками. На третьем месте комментарий Alexander Gutin с 58 лайками.

Комментарий Секе Қозған, набравший наибольшее количество лайков, представлял собой фразу “и тут появляется он…” и фотографию “Уятмена”. Пояснения смотрите далее.

Незадолго до того, как был сделан анализируемый пост, в казахстанском сегменте Facebook был еще один всплеск активности, связанный с человеком, который накрыл тканью статую девушки с заметными анатомическими деталями, потому что, по его словам, это был «уят» (стыд, позор). После этого он получил известность и прозвище «Уятмэн». Близость этих двух событий по времени в некоторой степени объясняет, почему именно такой комментарий получил больше всего откликов и лайков.

Давайте теперь посмотрим на самый длинный комментарий и его характеристики.

# most lengthy comment
comment_length = 0
for (i in 1:nrow(df_comments)) {
     if (nchar(df_comments$cmnt[i]) > comment_length) {
          comment_length <- nchar(df_comments$cmnt[i])
          index <- i
     }
}

# print out findings
sprintf("Author of the most lengthy comment is %s", df_comments$name[index])
number_of_words <- sapply(gregexpr("\\W+", df_comments$cmnt[index]), length) + 1
sprintf("The comment contains %d characters and %d words",
        nchar(df_comments$cmnt[index]), number_of_words)

# clean memory
rm(v)

Автором самого длинного комментария является Khairbek Kekenov. Комментарий содержит 1538 букв и 252 слов.

Текст комментария (орфография и пунктуация сохранены): “ПЕРВОЕ: -знание анатомии и физиологии человека: Здесь можно сказать следующее: НИКТО НЕ ЗАПРЕЩАЕТ, В ТОМ ЧИСЛЕ И КАЗАШКАМ «ЗНАНИЕ АНАТОМИИ И ФИЗИОЛОГИИ ЧЕЛОВЕКА». Скорее всего вы сачковали в школе?.. ВТОРОЕ: «Говорить на тему секса, ибо не скромно». НАВЕРНОЕ ЭТА ТЕМА НЕ ИМЕЕТ ОТНОШЕНИЯ К НАЦИОНАЛЬНОСТИ. Ибо эта тема интимная, и про это не обязательно ходить и кричать на всю страну. ЭТО НАВЕРНОЕ ВАМ и ТАК ПОНЯТНО, КАК БОЖИЙ ДЕНЬ. Если каждый будет ходить и кричать об этом как делаете ВЫ единственная , тогда наверно будет что-то вроде – СОДОМА И ГАММОРЫ. ИЛИ как на западе - ЗА СОБАЧКУ ЗА МУЖ ИДТИ. ЧТО КАСАЕТСЯ ВАШЕГО ТРЕТЬЕГО ВОПРОСА. «ОСВЕДОМЛЕННОСТЬ» – ЭТО ХОРОШО. Но наверное не обязательно показывать свою неудовлетворенность.. Если вы хотите больше секса- делайте это МОЛЧА. И просто задумайтесь. Может браки распадаются из-за того, что именно ваша половина нынешняя не грамотна. Согласен, фигура у вас прекрасная, но это только фигура на фото. А как вы ведете себя в постели?.. ДОСКА И ДВА СОСКА? ТО, что вы пишете – не касается мужчин. Вы сами изучите. Курсы там организуйте- для женщин..ЕСЛИ ВЫ ТАКАЯ МОЛОДАЯ И ГРАМОТНАЯ И ОПЫТНАЯ , В ВОПРОСАХ БРАКА И СЕМЬИ. Если вы думаете, что все женщины так думают, как думаете именно вы – ВЫ НЕ ПРАВЫ. ИБО вы потеряли СОВЕСТЬ. ЗНАЧИТ – ВСЁ потеряли. Есть такая пословица. «ПОКА СУЧКА НЕ ЗАХОЧЕТ, КАБЕЛЬ НЕ ВСКОЧИТ». ЗНАЧИТ ВЫ, ИМЕННО ВЫ, НЕ ТАК ХОТИТЕ, это не значит , что все так хотят как вы хотите. И не надо судить по себе. РАМКИ ПРИЛИЧИЯ НАХОДЯТСЯ ГОРАЗДО ВЫШЕ , ЧЕМ ВЫ ДУМАЕТЕ.”.

Исследование 4 - Облако слов

Завершить анализ хотелось наглядным представлением слов, чаще всего употребляемых в тексте комментариев. Для этого из всех слов всех комментариев был создан текстовый корпус, из которого были удалены так называемые “стоп-слова”, т.е. слова, не несущие смысловой нагрузки. Корпус был обработан и на его основе построен график 30 наиболее часто употребленных слов, а также наглядное облако слов.

# because we have relatively small number of documents we will use simple corpus
df_corpus = Corpus(VectorSource(df_comments$cmnt), readerControl = list(language = "rus"))

# load list of russian stop words
ru_stopwords <- readLines("/Volumes/data/projects/fb_sentiment/res/stop_words_ru.txt",
                          encoding = "UTF-8", skipNul = TRUE, warn = FALSE)
kz_stopwords <- readLines("/Volumes/data/projects/fb_sentiment/res/stop_words_kz.txt",
                          encoding = "UTF-8", skipNul = TRUE, warn = FALSE)

# remove contact information in the beginning of the file
ru_stopwords <- ru_stopwords[5:length(ru_stopwords)]
kz_stopwords <- kz_stopwords[5:length(kz_stopwords)]

# combine extended and standard stopwords lists
extended_stopwords <- c(stopwords('russian'), ru_stopwords, kz_stopwords)

# pre-process corpus
df_corpus <- tm_map(df_corpus, removeNumbers)
df_corpus <- tm_map(df_corpus, removePunctuation)
df_corpus <- tm_map(df_corpus, content_transformer(tolower))

# replace often misspelled significant word for correct spelling with diacritics
df_corpus <- tm_map(df_corpus, content_transformer(gsub),
                    pattern = "уят", replacement = "ұят")

# remove insignificant words
words_to_remove <- c("like","wink","smile","photo","emoticon")
df_corpus <- tm_map(df_corpus, removeWords, words_to_remove)

# remove stop words and extra spaces
df_corpus <- tm_map(df_corpus, removeWords, extended_stopwords)
df_corpus <- tm_map(df_corpus, stripWhitespace)

# create term-document matrix
tdm <- TermDocumentMatrix(df_corpus)

# remove sparse words:
# 0.99999 - remain all words, nothing is deleted
# 0.9999  - remain words encountered more than 2 times
# 0.999   - remain words encountered more than 10 times
tdm <- removeSparseTerms(tdm, 0.999)

# create data frame with words sorted by frequency
d <- sort_freq(tdm)

# show top words as bar plot
par(mar = c(3,6,3,1) + 0.1)
barplot(d[1:30,]$freq,
        names.arg = d$word[1:30],
        col = "lightgreen",
        xlim = c(0,600),
        ylim = c(35,0),
        horiz = TRUE,
        las = 1)
grid(NULL, NA, lwd = 1, col = "lightgray", lty = "dotted")
title("Топ 30 использованных слов", adj = 0, line = 0.5)

# build word cloud
set.seed(2017)
wordcloud2(data = d)

Механизм облака слов очень простой - чем больше раз определенное слово употреблялось, тем больше оно по размеру на этом облаке. Как мы можем видеть, самое большое слово - это “фото”. Можем сделать вывод, что пользователи больше всего обсуждали именно саму фотографию, а потом уже все остальные темы.

Анализ тональности

Во время работы над этой частью выяснилось, что для проведения подобного анализа нет подходящих ресурсов. Поэтому я публикую этот отчет «как есть», в то же время занимаясь подготовкой инструментов и данных для проведения анализа тональности текстов. Как только они будут готовы, я обновлю этот (и другие) отчеты.

Выводы

Ресурсы

Контакты

© 2017 Denis Rasulev